#pragma once

#include <gl\glew.h>
#include <vector>
#include "..\types.h"
#include "..\integer_conversion.h"

namespace blu
{
	namespace impl
	{
		::GLenum getComponentGLType(type_identity type_)
		{

		}

		::GLenum getComponentSize(type_identity type_)
		{

		}

		class FeedingPlace
		{
		private:
			::GLuint _binding;
		public:
			FeedingPlace(::GLuint binding_)
				:_binding(binding_)
			{

			}
			::GLuint binding() const
			{
				return _binding;
			}
		};

		class SingleInputConvention
		{
		private:
			::GLuint _binding, _stride;
		public:
			SingleInputConvention(::GLuint binding, ::GLuint stride)
				:_binding(binding), _stride(stride)
			{

			}
			::GLuint binding() const
			{
				return _binding;
			}
			::GLuint stride() const
			{
				return _stride;
			}
		};

		class ExternalInputLocation
		{
		private:
			::GLuint _loc;
		public:
			::GLuint location() const
			{
				return _loc;
			}
		};

		class SingleFetchConvention
		{
		private:
			type_identity _sourceType;
			integer_conversion _conv;
			::GLuint _loc, _binding, _offset;
		public:
			SingleFetchConvention(type_identity srctype, integer_conversion conv,
				::GLuint loc, ::GLuint binding, ::GLuint offset)
				:_sourceType(srctype), _conv(conv), _loc(loc), _binding(binding), _offset(offset)
			{

			}
			type_identity source_type() const
			{
				return _sourceType;
			}

			::GLuint location() const
			{
				return _loc;
			}

			::GLuint binding() const
			{
				return _binding;
			}
			::GLuint offset() const
			{
				return _offset;
			}
			integer_conversion conversion() const
			{
				return _conv;
			}
		};

		class InputConventionList
		{
		private:
			std::vector<SingleInputConvention> _convs;
		public:
			auto begin() const
			{
				return _convs.begin();
			}
			auto end() const
			{
				return _convs.end();
			}
			FeedingPlace alloc(std::uint32_t stride)
			{
				auto binding = static_cast<::GLuint>(_convs.size());
				_convs.push_back(SingleInputConvention(
					binding, static_cast<::GLuint>(stride)));
				return FeedingPlace(binding);
			}
		};

		class FetchConventionList
		{
		private:
			std::vector<SingleFetchConvention> _convs;
		public:
			auto begin() const
			{
				return _convs.begin();
			}
			auto end() const
			{
				return _convs.end();
			}

			void add(ExternalInputLocation dstloc,
				FeedingPlace feedplace,
				std::uint32_t placeoffset,
				type_identity type,
				integer_conversion conv)
			{
				_convs.push_back(SingleFetchConvention(
					type, conv, dstloc.location(), feedplace.binding(), placeoffset));
			}
		};

		class Pipeline
		{
		private:
			InputConventionList _inputConvs;
			FetchConventionList _feedConvs;
			::GLenum _primTopo;
		public:
			decltype(auto) inputConvsBegin() const
			{
				return _inputConvs.begin();
			}
			decltype(auto) inputConvsEnd() const
			{
				return _inputConvs.end();
			}
			decltype(auto) feedConvsBegin() const
			{
				return _feedConvs.begin();
			}
			decltype(auto) feedConvsEnd() const
			{
				return _feedConvs.end();
			}
			::GLenum getPrimTopo() const
			{
				return _primTopo;
			}
		};
	}
}